/*
  完成日志器模块
  1、抽象日志器基类
  2、派生出不同子类
*/
#include "util.cpp"
#include "level.cpp"
#include "format.cpp"
#include "sink.cpp"
#include <atomic>
#include <mutex>

namespace Mylog
{
    class LoggerFormatItem
    {
        public:
            using ptr = std::shared_ptr<Logger>;
            Logger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks):
            _logger_name(logger_name),
            _limit_level(level),
            _formatter(formatter),
            _sinks(sinks.begin(), sinks.end()) {}
            void debug(const std::string &file, size_t line, const std::string &fmt, ...)
           {
                if(LogLevel::value::DEBUG < _limit_level) {return ;}
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::DEBUG, file, line, res);
                free(res);
           }
            void info(const std::string &file, size_t line, const std::string &fmt, ...)
            {
                if(LogLevel::value::INFO < _limit_level) {return ;}
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::INFO, file, line, res);
                free(res);
           }
            void warn(const std::string &file, size_t line, const std::string &fmt, ...)
            {
                if(LogLevel::value::WARN < _limit_level) {return ;}
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::WARN, file, line, res);
                free(res);
           }
            void error(const std::string &file, size_t line, const std::string &fmt, ...)
            {
                if(LogLevel::value::ERROR < _limit_level) {return ;}
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::ERROR, file, line, res);
                free(res);
           }
            void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
            {
                if(LogLevel::value::FATAL < _limit_level) {return ;}
                va_list ap;
                va_start(ap, fmt);
                char *res;
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1)
                {
                    std::cout << "vasprintf failed\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::FATAL, file, line, res);
                free(res);
           }

        protected:
            void serialize(LogLevel::value::level, const std::string &file, size_t line, char *str)
            {
                LogMsg msg(LogLevel::value::DEBUG, line, file, _logger_name, str);
                std::stringstream ss;
                _formatter->format(ss, msg);
                log(ss.str().c_str(), ss.str.size());
            }
            virtual void log(const char *data, size_t len) = 0;
            
        protected:
            std::mutex _mutex;
            std::string _logger_name;
            std::atomic<Loglevel::value> _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;

    };

    class SyncLogger : public Logger
    {
        public:
            SyncLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks):
            Logger(logger_name, level, formatter, sinks) {}
        protected:
            virtual void log(const char *data, size_t len)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if(_sinks.empty())
                return;
                for(auto &sink : _sinks)
                {
                    sink->log(data, len);
                }
            }
    };

    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    /*使用建造者模式建造日志器，不用用户直接去构造，简化用户使用复杂度*/
    //1、设置一个日志器建造者类
    class LoggerBuilder
    {
        public:
            LoggerBuilder():
                _logger_type(LoggerType::LOGGER_SYNC),
                _limit_level(LogLevel::value::DEBUG){}
            void buildLoggerType(LoggerType type) { _logger_type = type; }
            void buildLoggerName(const std::string &name) { _logger_name = name; }
            void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }
            void buildFormatter(const std::string &pattern) { _formatter =  std::make_shared<Formatter>(pattern); }
            template<typename SinkType, typename ...Args>
            void buildsink(Args &&...args)
            {
                LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
                _sinks.push_back(psink);
            }
            virtual Logger::ptr build() = 0;
        private:
            LoggerType _logger_type;
            std::string _logger_name;
            Loglevel::value _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
    };
    //2、派生出具体的建造者类
    class LocalLoggerBuilder : public LoggerBuilder
    {
        public:
            Logger::ptr build() override
            {
                assert(_logger_name.empty() == false);
                if(_formatter.get() == nullptr)
                {
                    _formatter = std::make_shared<Formatter>();
                }
                if(_sinks.empyty())
                {
                    buildSink<StdoutSink>();
                } 
                if(_logger_type == LoggerType::LOGGER_ASYNC){
                }
                return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
    };
}